﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Net;
using System.Linq;
using System.Web;

namespace FreeSrun.Core
{
    public class Util
    {
        public static string LOGIN_URL = "";
        public static string LOGOUT_URL = "";
        private static string MSG_URL = "";
        public static string USERNAME = "";
        public static string PASSWORD = "";
        public static string SERVER_IP = "";
        public static int LOGIN_PORT = 3333;
        public static int HEARTBEAT_PORT = 3335;
        public static string UID = "";
        public static byte[] HEARTBEAT_PACKET = null;
        public static bool READY_TO_LOGIN = false;
        public static bool IS_LOGIN = false;
        public static long TIMESTAMP_OFFSET = 0;
        public static string HelpInfo
        {
            get
            {
                string info = "";
                info += "All parameters list:\n";
                info += "-u Username\n";
                info += "-p Password\n";
                info += "-add Server IP address\n";
                info += "[-lp] Login port, 3333 default\n";
                info += "[-hp] Heartbeat packets port, 3335 default\n";
                info += "[-nl] Notify balloon last time, in second, 3 default\n";
                info += "[-to] Timestamp offset between local time and server time\n";
                info += "[-help][-h][-?] Show the help info";
                return info;
            }
        }
        public static int NOTIFY_LAST = 3;
        public static SimpleLogger logger = new SimpleLogger();
        /// <summary>
        /// Build the login form body
        /// </summary>
        /// <param name="username"></param>
        /// <param name="encrptPassword"></param>
        /// <returns></returns>
        public static Dictionary<string, string> BuildForm(string username, string encrptPassword)
        {
            Dictionary<string, string> paramList = new Dictionary<string, string>();
            paramList.Add("username", username);
            paramList.Add("password", encrptPassword);
            paramList.Add("drop", "0"); // 是否访问免费资源，0否；1是
            paramList.Add("type", "2"); // 登录方式，1为WEB，2为客户端
            paramList.Add("n", "8");    // 客户端版本，8为WIN32
            paramList.Add("mac", ""); //mac地址，未绑定
            return paramList;
        }

        /// <summary>
        /// Login
        /// </summary>
        /// <param name="url"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static LoginResponseResult Login(string url, string username, string password)
        {
            PasswordEncryptor pe = new PasswordEncryptor();
            long ts = BuildTimestamp(TIMESTAMP_OFFSET);
            string enc_pwd = pe.Encrypt(password, ts);
            Dictionary<string, string> paramList = BuildForm(username, enc_pwd);
            string attempt = WebRequestHelper.RequestCreate(url, "POST", paramList);

            LoginResponseResult result = new LoginResponseResult(attempt);
            if (result.Status == ResponseStatus.Error)
            {
                long correctTime = result.Timestamp;
                enc_pwd = pe.Encrypt(password, Convert.ToInt64(correctTime));
                paramList["password"] = enc_pwd;
                string response = WebRequestHelper.RequestCreate(url, "POST", paramList);
                result = new LoginResponseResult(response);
                result.Timestamp = correctTime;
                result.TimeOffset = result.Timestamp - ts;
            }
            return result;
        }

        /// <summary>
        /// Build the heart beat packet
        /// </summary>
        /// <param name="uid"></param>
        /// <returns></returns>
        public static byte[] BuildHeartBeatPacket(string uid)
        {
            byte[] result = new byte[8];
            string hex_uid = Convert.ToString(Convert.ToInt64(uid), 16);
            while (hex_uid.Length < 16)
            {
                hex_uid = "0" + hex_uid;
            }

            int cnt = 0;
            for (int i = hex_uid.Length - 1; i > 0; i -= 2)
            {
                string tmp = new String(new char[] { hex_uid[i - 1], hex_uid[i] });
                result[cnt] = Convert.ToByte(tmp, 16);
                cnt++;
            }
            return result;
        }

        /// <summary>
        /// Initiate and start the timer
        /// </summary>
        public static void HeartBeat()
        {
            Timer keep = new Timer(1000 * 3 * 60);
            keep.AutoReset = true;
            keep.Elapsed += new ElapsedEventHandler(Keep_Elapsed);
            keep.Enabled = true;
        }

        /// <summary>
        /// Send UDP when timer is tick
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        static void Keep_Elapsed(object source, ElapsedEventArgs e)
        {
            try
            {
                byte[] data = SendUdp(SERVER_IP, HEARTBEAT_PORT, HEARTBEAT_PACKET);
            }
            catch (SocketException ex)
            {
                logger.AppendLog(ex);
            }
        }

        /// <summary>
        /// Send a UDP packet
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] SendUdp(string ip, int port, byte[] data)
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), port);
            UdpClient client = new UdpClient();

            client.Send(data, data.Length, ipep);
            client.Client.ReceiveTimeout = 3 * 60 * 1000;
            byte[] response = null;
            response = client.Receive(ref ipep);
            client.Close();
            return response;
        }

        /// <summary>
        /// Fetch notify messages form server
        /// </summary>
        /// <returns></returns>
        public static string GetNotifyMessage()
        {
            string result = "";
            result = WebRequestHelper.RequestCreate(Util.MSG_URL, "GET", "", Encoding.ASCII, Encoding.GetEncoding("GB2312"));
            result = HttpUtility.HtmlDecode(result);
            return result;

        }

        /// <summary>
        /// Build timestamp without offset. It's the total seconds from 1970-1-1 to now
        /// </summary>
        /// <returns></returns>
        public static long BuildTimestamp()
        {
            TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1);
            long result = Convert.ToInt64(ts.TotalSeconds);
            return result;
        }

        /// <summary>
        /// Build timestamp with the given offset
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public static long BuildTimestamp(long offset)
        {
            return BuildTimestamp() + offset;
        }

        /// <summary>
        /// Post uid to logout
        /// </summary>
        /// <param name="url">Logout URL</param>
        /// <param name="uid">UID, it will be recieved after login</param>
        /// <returns>logout_ok / logout_error</returns>
        public static LogoutResponseResult Logout(string url, string uid)
        {
            Dictionary<string, string> form = new Dictionary<string, string>();
            form.Add("uid", uid);
            string responseText = WebRequestHelper.RequestCreate(LOGOUT_URL, "POST", form);
            LogoutResponseResult result;
            try
            {
                result = new LogoutResponseResult(responseText);
                return result;
            }
            catch (Exception ex)
            {
                result = new LogoutResponseResult(ResponseStatus.Error, ex.Message);
                result.ResponseText = responseText;
                return result;
            }
        }

        /// <summary>
        /// Leagal Param:
        ///     -u Username
        ///     -p Password
        ///     -add Server IP address"
        ///     [-lp] Login port, 3333 default
        ///     [-hp] Heartbeat packets port, 3335 default
        ///     [-nl] Notify balloon last time, in second, 3 default
        ///     [-to] Timestamp offset between local time and server time";
        ///     [-h/-?/-help] Show the help info
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public static bool CheckParam(string[] args)
        {
            bool result = true;
            if (args.Length % 2 == 1 || args.Length == 0)
            {
                result = false;
            }
            if (!(args.Contains("-u") && args.Contains("-p") && args.Contains("-add")))
            {
                result = false;
            }
            return result;
        }

        /// <summary>
        /// Configure the input arguments, initiate some constant strings.
        /// </summary>
        /// <param name="args"></param>
        public static void Configure(string[] args)
        {
            Dictionary<string, string> list = new Dictionary<string, string>();
            for (int i = 0; i < args.Length; i += 2)
            {
                list.Add(args[i], args[i + 1]);
            }
            USERNAME = list["-u"];
            PASSWORD = list["-p"];
            SERVER_IP = list["-add"];

            if (list.Keys.Contains("-lp"))
            {
                LOGIN_PORT = Convert.ToInt32(list["-lp"]);
            }
            if (list.Keys.Contains("-hp"))
            {
                LOGIN_PORT = Convert.ToInt32(list["-hp"]);
            }
            if (list.Keys.Contains("-nl"))
            {
                NOTIFY_LAST = Convert.ToInt32(list["-nl"]);
                if (NOTIFY_LAST < 0)
                {
                    NOTIFY_LAST = 0;
                }
            }
            if (list.Keys.Contains("-to"))
            {
                TIMESTAMP_OFFSET = Convert.ToInt64(list["-to"]);
            }
            LOGIN_URL = "http://" + SERVER_IP + ":" + LOGIN_PORT + "/cgi-bin/do_login";
            LOGOUT_URL = "http://" + SERVER_IP + ":" + LOGIN_PORT + "/cgi-bin/do_logout";
            MSG_URL = "http://" + SERVER_IP + ":" + LOGIN_PORT + "/get_msg.php";
            READY_TO_LOGIN = true;
        }
    }
}
